package bhwz.seac3.util;

import java.util.*;
import java.util.function.*;

/**
 * firstly run preCommandProcessor<br>
 * and then run all executeWays<br>
 * and then run postCommandProcessor<br>
 * finally run all sub executers
 */
public class RecursiveCommandExecuter<CommandType> {
	
	public static interface ExecuteWay<Command>{
		public void execute(Command executeWay) throws Exception;
	}
	
	protected List<ExecuteWay<CommandType>> executeWays = new ArrayList<>();
	protected Predicate<CommandType> formateChecker;
	protected UnaryOperator<CommandType> preCommandProcessor;
	protected UnaryOperator<CommandType> postCommandProcessor;
	protected List<RecursiveCommandExecuter<CommandType>> subExecuters = new ArrayList<>();
	public RecursiveCommandExecuter() {
	}
	
	public void addExecuteWay(ExecuteWay<CommandType> executeWay) {
		executeWays.add(executeWay);
	}

	public void removeExecuteWay(ExecuteWay<CommandType> executeWay) {
		executeWays.remove(executeWay);
	}

	public void setFormateChecker(Predicate<CommandType> formateChecker) {
		this.formateChecker = formateChecker;
	}

	public void setPreCommandProcessor(
			UnaryOperator<CommandType> preCommandProcessor) {
		this.preCommandProcessor = preCommandProcessor;
	}

	public void setPostCommandProcessor(
			UnaryOperator<CommandType> postCommandProcessor) {
		this.postCommandProcessor = postCommandProcessor;
	}

	public void addSubExecuter(RecursiveCommandExecuter<CommandType> subExecuter) {
		subExecuters.add(subExecuter);
	}

	public void removeSubExecuter(
			RecursiveCommandExecuter<CommandType> subExecuter) {
		subExecuters.remove(subExecuter);
	}

	public void execute(CommandType cmd) throws Exception {
		if (cmd == null)
			return;
		if (preCommandProcessor != null)
			cmd = preCommandProcessor.apply(cmd);
		if (formateChecker == null || formateChecker.test(cmd)) {
			for (ExecuteWay<CommandType> executeWay : executeWays) {
				if (executeWay != null)
						executeWay.execute(cmd);
			}
			if (postCommandProcessor != null)
				cmd = postCommandProcessor.apply(cmd);
			for (RecursiveCommandExecuter<CommandType> ce : subExecuters) {
				if (ce != null)
					ce.execute(cmd);
			}
		}
	}

}
